package com.flypigs.lock;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.CacheConfiguration;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
public class EhcacheLock implements DistributedLock {
    private CacheManager cacheManager;
//    private Cache<String,EhcacheValue> cache;
    private Cache cache;
    private String lockKey;
    private String lockValue = UUID.randomUUID().toString();
    private EhcacheValue ehcacheValue;
    private boolean locked = false;
    private boolean unLocked = false;
    //多例锁，一把只能执行一次；
    private AtomicBoolean tried = new AtomicBoolean(false);

    public EhcacheLock(CacheManager cacheManager,Cache cache) {
        Assert.notNull(cacheManager,"cacheManager不能为空");
        this.cacheManager = cacheManager;
        this.cache = cache;
    }

    /**
     * 加锁
     *
     * ehcache不支持单个元素的过期时间设置；
     * 解决方案：
     * 1: 提前 配置不同过期时间的cache；使用时根据指定时间选择不同的cache
     * 2: 动态 根据不同时间，创建不同过期时间的cache；
     * 3：业务中设置对象时添加过期时间和创建时间，查询时二次判断；
     * 4: 使用过期策略
     * 本锁采用第4种：这就要求ehcahe配置中：必须增加创建一个cache：value类型必须为EhcacheValue类型，过期策略必须指定为EhcacheValueExpiryPolicy
     * @param lock
     * @param expireSeconds
     * @return
     */
    public boolean tryLock(final String lock,Integer expireSeconds){
        Assert.notNull(StringUtils.isNotBlank(lock),"lock不能为空");
//        lockKey = lock.intern();//运用常量池，保证相同内容的字符串是同一个串；
        lockKey = lock;
        if(tried.get()){
            throw new RuntimeException("一把锁只允许锁一次，请不要重复操作");
        }
        tried.set(true);

        try {
            final Integer expireSecondsTemp = expireSeconds == null || expireSeconds <= 0 ? 5 : expireSeconds;
            this.ehcacheValue = new EhcacheValue<Object>(lockValue,expireSecondsTemp);
            if(locked) {
                log.debug("又进来了");
            }

            Object oldObj = this.cache.putIfAbsent(lockKey,ehcacheValue);
            if(oldObj != null && !(oldObj instanceof EhcacheValue)){
                throw new RuntimeException("分布锁：[" + lockKey + "]与其它类型的缓存key冲突，冲突的缓存值类型为：" + oldObj.getClass().getName());
            }
            if(oldObj == null){
                locked = true;
            }
        }catch (Exception e){
            log.warn("加锁失败" + e.getMessage());
            locked = false;
        }
        return locked;
    }

    public boolean unlock() {
        if (!locked) {
            return false;
        }
        try {
            unLocked = this.cache.remove(lockKey, ehcacheValue);
        }catch (Exception e){
            log.warn("解锁失败:" + e.getMessage());
            unLocked = false;
        }
        return unLocked;
    }

    public boolean isLocked() {
        return locked;
    }

    public boolean isUnLocked() {
        return unLocked;
    }
}
